package com.dysoft.flesh;

import com.dysoft.bones.DataManager;
import com.dysoft.flesh.gl.GLObject;
import com.dysoft.flesh.gl.GLRenderer;
import org.apache.log4j.Logger;

import javax.media.opengl.GL;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Sean Micklethwaite
 *         Nov 16, 2010 4:08:07 PM
 */
public abstract class AbstractRenderer implements Renderer.Root {
	final Queue<Operation> preRenderOps = new LinkedList<Operation>();
	final Queue<Operation> preRenderOpsToAdd = new LinkedList<Operation>();

	boolean preRender = false;
	final static Logger LOG = Logger.getLogger(GLRenderer.class);
	Renderer root = new Renderer() {
		public void render(RenderContext ctx) throws DataManager.DataObject.NotPresentException { }
	};
	private long previousTime = System.currentTimeMillis();

	public AbstractRenderer(Renderer renderer) {
		if(renderer != null) this.root = renderer;
	}

	public void queuePreRenderOp(Operation op) {
		synchronized (preRenderOps) {
			assert !preRenderOps.contains(op) && !preRenderOpsToAdd.contains(op);
			if (preRender) { // TODO: multithreading (only one GL thread?)
				try {
					if(op.call(getContext())) {
						preRenderOpsToAdd.add(op);
					}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			} else {
				preRenderOps.add(op);
			}
		}
	}

	public void activate(RenderContext ctx) {
		synchronized (preRenderOps) {
			preRender = true;
			for(Iterator<Operation> i = preRenderOps.iterator(); i.hasNext();) {
				try {
					if(!i.next().call(ctx))
						i.remove();
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
			preRenderOps.addAll(preRenderOpsToAdd);
			preRenderOpsToAdd.clear();
			preRender = false;
		}

		long time = System.currentTimeMillis();
        update(time - previousTime);
        previousTime = time;
	}

	protected abstract void update(long dt);
	public abstract RenderContext getContext();

	public void render(RenderContext ctx) {
		try {
			root.render(ctx);
		} catch (DataManager.DataObject.NotPresentException e) {
			LOG.warn("Render pass aborted", e);
		}
	}

	public Renderer getRoot() {
		return root;
	}

	public void setRoot(Renderer root) {
		this.root = root;
	}
}
